package ray;

import java.util.List;
import light.Light;
import math.Vecmath;
import prim.Primitive;
import render.ColorMap;

public class RayTracer
{
    
    private static final int RAY_SAMPLES_X = 4;
    private static final int RAY_SAMPLES_Y = 4;
    private static final double REC_RAY_SAMPLES = (
        1.0 / (RayTracer.RAY_SAMPLES_X * RayTracer.RAY_SAMPLES_Y)
    );
    
    private int numPixelsX;
    private int numPixelsY;
    
    private double aspectRatio;
    
    private double stepX;
    private double stepY;
    
    private double[] positionView;
    
    private List<Primitive> listPrimitives;
    public void setPrimitives(List<Primitive> list) { this.listPrimitives = list; }
    
    private List<Light> listLights;
    public void setLights(List<Light> list) { this.listLights = list; }
    
    public RayTracer(
            int pixelsWide, 
            int pixelsTall)
    {
        this.numPixelsX = pixelsWide;
        this.numPixelsY = pixelsTall;
        
        this.aspectRatio = ((double) this.numPixelsX) / this.numPixelsY;
        
        this.stepX = 1.0 / this.numPixelsX;
        this.stepY = 1.0 / this.numPixelsY;
        
        this.positionView = new double[]
        {
            0.0, 0.0, -1.57
        };
    }
    
    public void traceRaysAt(
            int indexY, 
            ColorMap colorMap)
    {
        if ((indexY + 1) % (this.numPixelsY / 10) == 0)
        {
            System.out.println(
                "RayTracing: "
                + String.format("%.1f", 100.0 * (indexY + 1) / this.numPixelsY)
                + "%"
            );
        }
        
        for (int sampleY = 0; sampleY < RayTracer.RAY_SAMPLES_Y; sampleY++)
        {
            double pixOffsetY = 0.5 + 1.0 * sampleY / RayTracer.RAY_SAMPLES_Y - 1.0;
            double posStepY = 2.0 * this.stepY * (indexY + pixOffsetY) - 1.0;
            for (int sampleX = 0; sampleX < RayTracer.RAY_SAMPLES_X; sampleX++)
            {
                double pixOffsetX = 0.5 + 1.0 * sampleX / RayTracer.RAY_SAMPLES_X - 1.0;
                for (int indexX = 0; indexX < this.numPixelsX; indexX++)
                {
                    double posStepX = 2.0 * this.stepX * (indexX + pixOffsetX) - 1.0;
                    
                    double[] toPoint = new double[] {
                        this.aspectRatio * posStepX, 
                        posStepY, 
                        0.0
                    };
                    
                    // Generate ray
                    double[] rayDir = Vecmath.subtract(
                        toPoint, 
                        this.positionView
                    );
                    Ray ray = new Ray(this.positionView, rayDir);
                    
                    // Trace the ray through space
                    double[] pixelColor = new double[3];
                    this.traceRay(
                        ray, // Ray
                        pixelColor // Pixel Color storage
                    );
                    
                    // Color the pixel at indexX, indexY
                    Vecmath.scale(pixelColor, RayTracer.REC_RAY_SAMPLES);
                    colorMap.addColor(indexX, indexY, pixelColor);
                }
            }
        }
    }
    
    public void traceRay(
            Ray ray, 
            double[] pixelColor)
    {
        // Search for lowest T value in all primitives
        double minimumT = Double.MAX_VALUE;
        Primitive intersectPrim = null;
        for (Primitive primitive : this.listPrimitives)
        {
            double t = primitive.getIntersectT(ray);
            if (t < minimumT && t >= 0.0)
            {
                minimumT = t;
                intersectPrim = primitive;
            }
        }
        Light intersectLight = null;
        for (Light light : this.listLights)
        {
            double t = light.getIntersectT(ray);
            if (t < minimumT && t >= 0.0)
            {
                minimumT = t;
                intersectLight = light;
            }
        }
        
        if (intersectLight != null)
        {
            Vecmath.translate(pixelColor, intersectLight.getColor());
            return;
        }
        
        // If intersectPrim is null, then no intersection occurred
        if (intersectPrim == null)
        {
            return; // So, exit the method
        }
        // Save the intersect point
        double[] intersectPoint = ray.getPointAt(minimumT);
        
        // Lookup color from photon maps
        double[] lookupColor = intersectPrim.getColor(intersectPoint);
        
        // Color the pixel
        Vecmath.translate(pixelColor, lookupColor);
    }
    
    
    
}
